<template>
  <div class="staticServer">
    <el-container direction="vertical">
      <el-header>
        <h3>文件接收服务器</h3>
      </el-header>
      <el-tabs
        addable
        editable
        stretch
        v-model="currentIndex"
        type="card"
        @tab-remove="deleteTask"
        @tab-add="addTask"
      >
        <el-tab-pane
          v-for="(item, index) in myTasks"
          :key="index"
          :name="index.toString()"
          :label="item.name"
          :lazy="true"
          v-loading="item.loading"
        >
          <transition name="el-fade-in-linear">
            <el-main>
              <div
                class="show-loading"
                v-if="item.success"
                v-loading="true"
                element-loading-text="服务器正在运行"
                element-loading-spinner="el-icon-loading"
                :span="24"
              ></div>

              <el-form ref="form">
                <!--                   
                <el-form-item>
                  <el-input
                    :disabled="item.success"
                    v-model="item.name"
                    placeholder="服务器名称"
                  >
                    <span slot="prepend">服务器名称</span>
                  </el-input>
                </el-form-item>
                -->

                <el-form-item>
                  <el-input
                    :disabled="item.success"
                    v-model="item.localPath"
                    placeholder="工作文件夹"
                  >
                    <span slot="prepend">工作文件夹</span>
                    <el-button
                      :disabled="item.success"
                      class="el-icon-folder-opened"
                      :data-id="index"
                      slot="append"
                      plain
                      size="small"
                      @click="selectLocalPath"
                    ></el-button>
                  </el-input>
                </el-form-item>
                <el-form-item>
                  <el-input
                    :disabled="item.success"
                    v-model="item.extentionPath"
                    placeholder="存放文件夹"
                  >
                    <span slot="prepend">存放文件夹</span>
                  </el-input>
                </el-form-item>
                <el-form-item>
                  <el-input-number
                    :disabled="item.success"
                    v-model.number="item.portNumber"
                    :step="1"
                    :max="65536"
                    :min="0"
                    step-strictly
                    placeholder="端口号"
                  >
                    <span slot="prepend">端口号</span>
                  </el-input-number>
                </el-form-item>
              </el-form>
              <el-row v-if="!item.success">
                <el-col :span="24">
                  <el-button
                    :data-id="currentIndex"
                    class="el-icon-switch-button"
                    type="primary"
                    @click="startService"
                  >
                    {{ `启动服务器 (端口 :${item.portNumber})` }}
                  </el-button>
                </el-col>
              </el-row>
              <el-form class="sec-form" v-if="item.success">
                <el-form-item>
                  <el-input v-model="additionText" placeholder="后缀或文件名">
                    <span slot="prepend">后缀</span>
                  </el-input>
                </el-form-item>
              </el-form>
              <el-main
                v-for="(element, index) in networkInterfaces"
                :key="index"
              >
                <el-row
                  v-if="
                    undefined !== element &&
                      undefined !== element.address &&
                      item.success
                  "
                >
                  <el-col :span="24">
                    <el-button
                      :disabled="!item.success"
                      @click="openUrl"
                      type="primary"
                      size="small"
                      plain
                    >
                      http://{{ element.address
                      }}{{ item.portNumber == 80 ? "" : ":" + item.portNumber
                      }}{{ additionText }}
                    </el-button>
                  </el-col>
                </el-row>
                <el-row
                  v-if="
                    undefined !== element &&
                      undefined !== element.address &&
                      item.success
                  "
                >
                  <el-col :span="24">
                    <vue-qr
                      :text="
                        `http://${element.address}${
                          item.portNumber == 80 ? '' : ':' + item.portNumber
                        }${additionText}`
                      "
                    ></vue-qr>
                  </el-col>
                </el-row>
              </el-main>
            </el-main>
          </transition>
        </el-tab-pane>
      </el-tabs>
    </el-container>
  </div>
</template>

<script>
const bodyParser = require("body-parser");
const multer = require("multer");
const net = require("net");
const {
  // eslint-disable-next-line no-unused-vars
  desktopCapturer,
  // eslint-disable-next-line no-unused-vars
  ipcRenderer,
  remote,
  // eslint-disable-next-line no-unused-vars
  webFrame,
  // eslint-disable-next-line no-unused-vars
  clipboard,
  shell
} = require("electron");
const express = require("express");
const path = require("path");
const os = require("os");
const fs = require("fs-extra");
import VueQr from "vue-qr";
import { readdirSync } from "fs";
// eslint-disable-next-line no-unused-vars
function padding(num, length) {
  let decimal = num / Math.pow(10, length);
  //toFixed指定保留几位小数
  decimal = decimal.toFixed(length) + "";
  return decimal.substr(decimal.indexOf(".") + 1);
}
String.prototype.replaceAll = function(FindText, RepText) {
  const regExp = new RegExp(FindText, "g");
  return this.replace(regExp, RepText);
};
Date.prototype.format = function(fmt) {
  let o = {
    "M+": this.getMonth() + 1, //月份
    "d+": this.getDate(), //日
    "h+": this.getHours(), //小时
    "m+": this.getMinutes(), //分
    "s+": this.getSeconds(), //秒
    "q+": Math.floor((this.getMonth() + 3) / 3), //季度
    S: this.getMilliseconds() //毫秒
  };
  if (/(y+)/.test(fmt)) {
    fmt = fmt.replace(
      RegExp.$1,
      (this.getFullYear() + "").substr(4 - RegExp.$1.length)
    );
  }
  for (let k in o) {
    if (new RegExp("(" + k + ")").test(fmt)) {
      fmt = fmt.replace(
        RegExp.$1,
        RegExp.$1.length == 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length)
      );
    }
  }
  return fmt;
};

export default {
  name: "file-reveiver",
  data: function() {
    return {
      currentIndex: 0,
      additionText: "",
      myTasks: [
        this.$obs({
          name: "服务器1",
          localPath: remote.app.getPath("documents"),
          extentionPath: "服务器收到的文件",
          portNumber: 80 /* Math.round(Math.random() * 65536)*/,
          app: express(),
          success: false,
          loading: false
        }),
        this.$obs({
          name: "服务器2",
          localPath: remote.app.getPath("appData"),
          extentionPath: "服务器收到的文件",
          portNumber: 1002 /*Math.round(Math.random() * 65536)*/,
          app: express(),
          success: false,
          loading: false
        }),
        this.$obs({
          name: "服务器3",
          localPath: remote.app.getPath("cache"),
          extentionPath: "服务器收到的文件",
          portNumber: Math.round(Math.random() * 65536),
          app: express(),
          success: false,
          loading: false
        })
      ]
    };
  },
  computed: {
    networkInterfaces: function() {
      const _net = Object.values(os.networkInterfaces());
      let temp = [];
      _net.forEach((element1) => {
        element1.forEach((element2) => {
          if (
            element2.family !== undefined &&
            element2.family.toUpperCase() === "IPV4"
          ) {
            temp.push(element2);
          }
        });
      });
      // console.log("networkInterfaces", temp);
      return temp;
    }
  },
  components: {
    VueQr
  },
  methods: {
    folderSorter: function(arr) {
      let sorter = (a, b) => {
        try {
          return a.name.localeCompare(b.name);
        } catch (error) {
          return false;
        }
      };
      let folders = arr
        .filter((ele) => {
          return ele.isDirectory;
        })
        .sort(sorter);
      let files = arr
        .filter((ele) => {
          return !ele.isDirectory;
        })
        .sort(sorter);
      return folders.concat(files);
    },
    addTask: function() {
      this.myTasks.push(
        this.$obs({
          name: "服务器" + (this.myTasks.length + 1),
          localPath: remote.app.getPath("documents"),
          extentionPath: "服务器收到的文件",
          portNumber: Math.round(Math.random() * 65536),
          app: express(),
          success: false,
          loading: false
        })
      );
      this.currentIndex = (this.myTasks.length - 1).toString();
    },
    deleteTask: function(e) {
      if (!this.myTasks[e].success && !this.myTasks[e].loading) {
        this.myTasks.splice(e, 1);
        this.currentIndex = (e - 1).toString();
      } else {
        this.$message.error(`${this.myTasks[e].name}已经在运行, 无法删除`);
      }
    },
    selectLocalPath: async function(e) {
      let i = parseInt(e.target.dataset.id);
      let res = await remote.dialog.showOpenDialog({
        properties: ["openDirectory"]
      });
      if (!res.canceled) {
        this.$set(
          this.myTasks[i],
          "localPath",
          path.normalize(res.filePaths[0])
        );
      }
      // console.log(res);
    },
    getMulterStorage(serverSettings) {
      let localPath = path.normalize(serverSettings.localPath);
      fs.ensureDirSync(localPath);
      return multer.diskStorage({
        destination: (req, file, cb) => {
          let a = new Date();
          // a = localPath + serverSettings.extentionPath + a.format("yyyy-MM-dd");
          a = path.join(
            localPath,
            serverSettings.extentionPath,
            a.format("yyyy-MM-dd")
          );
          fs.ensureDirSync(a);
          cb(null, a); // 保存的路径，备注：需要自己创建
        },
        filename: (req, file, cb) => {
          let _fileName = file.originalname;
          let _b = new Date().format("yyyy-MM-dd");
          let _a = path.join(
            localPath,
            serverSettings.extentionPath,
            _b,
            _fileName
          );
          // localPath + serverSettings.extentionPath + _b + "/" + _fileName;
          let _spliter = [];
          while (fs.existsSync(_a)) {
            console.log("文件已经存在，正在重命名", _a);
            _spliter = _fileName.split(".");
            _spliter[0] += `@${new Date().format("hh：mm：ss")}`;
            _fileName = _spliter.join(".");
            _a = path.join(
              localPath,
              serverSettings.extentionPath,
              _b,
              _fileName
            );
          }
          cb(null, _fileName /*file.originalname*/);
        }
      });
    },
    setAppMulter(serverSettings) {
      let app = serverSettings.app;
      app.post(
        "/upload",
        multer({ storage: this.getMulterStorage(serverSettings) }).array(
          "file",
          100
        ),
        // eslint-disable-next-line no-unused-vars
        (req, res, next) => {
          // 设置响应类型、编码
          console.log("收到文件", req);
          //destination: "E:\temp\测试\up/2020-08-08"
          // encoding: "7bit"
          // fieldname: "file"
          // filename: "Office Lens_20150507_205351_processed.jpg"
          // mimetype: "image/jpeg"
          // originalname: "Office Lens_20150507_205351_processed.jpg"
          // path: "E:\temp\测试\up\2020-08-08\Office Lens_20150507_205351_processed.jpg"
          // size: 776685
          res.set({
            "content-type": "application/json; charset=utf-8"
          });
          res.end("成功");
          if (req.files[0]) {
            let notification = new Notification("服务器通知", {
              title: "服务器",
              subtitle: "文件接收通知",
              silent: false,
              body: `收到文件<${req.files[0].filename}>`
            });
            notification.show && notification.show();
          }
          this.$notify({
            title: "成功",
            message: `收到文件<${req.files[0].filename}>`,
            type: "success"
          });
          // shell.beep();
        }
      );
    },
    setAppPageStatic(serverSettings) {
      let app = serverSettings.app;
      let localPath = path.normalize(serverSettings.localPath);
      let _pagePath =
        -1 != `${os.type}`.toLowerCase().indexOf("dow") //仅Windows系统可以这样操作，其他系统需要制定up网页目录
          ? path.dirname(remote.app.getPath("exe")) + "/pages"
          : localPath + "/pages"; //仅Windows系统可以这样操作，其他系统需要制定up网页目录
      _pagePath = path.normalize(_pagePath);
      fs.ensureDirSync(_pagePath);
      app.use("/", express.static(_pagePath));
      console.log("服务器网页路径", _pagePath);
      app.use("/files", express.static(localPath));
    },
    getFolderChildrenAnothorTime: function(localPath, relPath) {
      let myPath = path.normalize(path.join(localPath, relPath));
      let _names = readdirSync(myPath);
      return this.folderSorter(
        _names.map((ele) => {
          let _path = path.join(myPath, ele); //该文件(夹)的绝对路径
          let isDirectory = fs.statSync(_path); //读取该路径参数
          isDirectory.isDirectory && (isDirectory = isDirectory.isDirectory()); //如果该路径是目录
          return isDirectory
            ? {
                name: ele,
                relPath: path.relative(localPath, _path), //从程序文件夹到该路径的绝对路径
                base: relPath, //base就是post来的文件夹
                isDirectory,
                children: []
              }
            : {
                name: ele,
                relPath: path.relative(localPath, _path), //从程序文件夹到该路径的绝对路径
                base: relPath, //base就是post来的文件夹
                isDirectory
              };
        })
      );
    },
    getFolderChildren: function(localPath, relPath) {
      let myPath = path.normalize(path.join(localPath, relPath));
      let _names = readdirSync(myPath);
      return _names.map((ele) => {
        let _path = path.join(myPath, ele); //该文件(夹)的绝对路径
        let isDirectory = fs.statSync(_path); //读取该路径参数
        isDirectory.isDirectory && (isDirectory = isDirectory.isDirectory()); //如果该路径是目录
        return isDirectory
          ? {
              name: ele,
              relPath: path.relative(localPath, _path), //从程序文件夹到该路径的绝对路径
              base: relPath, //base就是post来的文件夹
              isDirectory,
              children: this.getFolderChildrenAnothorTime(
                localPath,
                path.relative(localPath, _path)
              )
            }
          : {
              name: ele,
              relPath: path.relative(localPath, _path), //从程序文件夹到该路径的绝对路径
              base: relPath, //base就是post来的文件夹
              isDirectory
            };
      });
    },
    getFolder: function({ localPath }, { relPath }) {
      let myPath = path.normalize(path.join(localPath, relPath));
      let _names = readdirSync(myPath);
      return _names.map((ele) => {
        let _path = path.join(myPath, ele); //该文件(夹)的绝对路径
        let isDirectory = fs.statSync(_path); //读取该路径参数
        isDirectory.isDirectory && (isDirectory = isDirectory.isDirectory()); //如果该路径是目录
        return isDirectory
          ? {
              name: ele,
              relPath: path.relative(localPath, _path), //从程序文件夹到该路径的绝对路径
              base: relPath, //base就是post来的文件夹
              isDirectory,
              // children: this.getFolder(
              //   { localPath },
              //   { relPath: path.relative(localPath, _path) }
              // )
            }
          : {
              name: ele,
              relPath: path.relative(localPath, _path), //从程序文件夹到该路径的绝对路径
              base: relPath, //base就是post来的文件夹
              isDirectory
            };
      });
    },
    setAppReadFolder(serverSettings) {
      let { app } = serverSettings;
      const jsonParser = bodyParser.json();
      app.post("/getFolder", jsonParser, async ({ body }, res) => {
        // console.log(req.body); //req.body就是exactly发来的数据
        let folderData = this.getFolder(serverSettings, body);
        console.log(folderData);
        res.send({ folderData }); //返回数据,axios.post之后then用res.data来接收
        res.end("成功");
      });
    },
    setAppConfigure(serverSettings) {
      let { app } = serverSettings;
      app.use((req, res, next) => {
        res.header("Access-Control-Allow-Origin", "*");
        res.header(
          "Access-Control-Allow-Methods",
          "PUT, GET, POST, DELETE, OPTIONS"
        );
        res.header("Access-Control-Allow-Headers", "X-Requested-With");
        res.header("Access-Control-Allow-Headers", "Content-Type");
        next();
      });
    },
    startServer: async function(serverSettings) {
      let app = serverSettings.app;
      let portNumber = serverSettings.portNumber;
      return new Promise((resolve1, reject) => {
        try {
          this.setAppConfigure(serverSettings);
          this.setAppPageStatic(serverSettings);
          this.setAppMulter(serverSettings);
          this.setAppReadFolder(serverSettings);
          // eslint-disable-next-line no-unused-vars
          app.listen(portNumber, (res) => {
            console.log(`App listening at port ${portNumber}`);
            this.$set(serverSettings, "success", true);
            this.$set(serverSettings, "loading", false);
            resolve1(true);
            this.$notify({
              title: "成功",
              message: `开启<${serverSettings.name}>成功`,
              type: "success"
            });
          });
        } catch (err) {
          console.error(err);
          resolve1(false);
          reject(false);
          this.$set(serverSettings, "success", false);
          this.$set(serverSettings, "loading", false);
        }
      });
    },
    startService: async function() {
      let i = parseInt(this.currentIndex);
      this.$set(this.myTasks[i], "loading", true);
      let _ok = false;
      let _annonceFailure = () => {
        this.$set(this.myTasks[i], "success", false);
        this.$set(this.myTasks[i], "loading", false);
        this.$notify({
          title: "失败",
          message: `开启<${this.myTasks[i].name}>失败`,
          type: "error"
        });
      };
      try {
        _ok = await this.startServer(this.myTasks[i]);
      } catch (err) {
        console.error(err);
        _annonceFailure();
        _ok = true;
      }
      _ok || _annonceFailure();
    },
    pauseServiceClick: async function() {
      let i = parseInt(this.currentIndex);
      // let _app = this.myTasks[i].app;
      // console.log(_app);
      this.$set(this.myTasks[i], "success", false);
    },
    portInUse: async function(port) {
      return new Promise((resolve, reject) => {
        let server = net.createServer().listen(port);
        // console.log(server);
        server.on("listening", () => {
          // console.log(`the server is running on port ${port}`);
          server.close();
          resolve(port);
        });
        server.on("error", (err) => {
          // console.log(`this port ${port} is occupied.try another.`);
          if (err.code == "EADDRINUSE") {
            port++;
            reject(err);
          }
        });
      });
    },
    openUrl: function(e) {
      // console.log(e);
      try {
        shell.openExternal(e.target.textContent);
      } catch (error) {
        console.warn(error);
      }
    }
  }
};
</script>
